package org.login.handler;

import lombok.extern.slf4j.Slf4j;
import org.common.dispatcher.CMD;
import org.common.handler.BaseProtoHandler;
import org.common.mongo.MongoService;
import org.common.mongo.collection.AccountCollection;
import org.common.mongo.collection.SimpleRoleCollection;
import org.common.netty.Pack;
import org.common.netty.PackCodec;
import org.common.utils.GenIdUtils;
import org.common.utils.SpringUtils;
import org.common.utils.StringChecker;
import org.login.GameServerManger;
import org.login.akka.ConnectActorManager;
import org.login.defs.ConnectStatusEnum;
import org.login.obj.ConnectContext;
import org.login.obj.GameServerContext;
import org.protobuf.AccountMsg;
import org.protobuf.InnerServerMsg;
import org.protobuf.ProtoEnumMsg;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.stereotype.Component;

import java.util.List;


/**
 * Player相关协议处理
 */
@Slf4j
@Component
public class LoginProtoHandler extends BaseProtoHandler {

    // region 账号注册

    @CMD(ProtoEnumMsg.CMD.ID.ACCOUNT_REGISTER_VALUE)
    public Pack onAccountRegisterMsg(ConnectContext cc, AccountMsg.C2SAccountRegister up) {
        log.info("player register, accountName = {}, password = {}", up.getAccountName(), up.getPassword());
        boolean res = accountRegister(up.getAccountName().trim(), up.getPassword().trim());
        // 回包
        AccountMsg.S2CAccountRegister.Builder builder = AccountMsg.S2CAccountRegister.newBuilder();
        builder.setSuccess(res);
        return new Pack(ProtoEnumMsg.CMD.ID.ACCOUNT_REGISTER_VALUE, builder.build().toByteArray());
    }

    private boolean accountRegister(String accountName, String password) {
        if (StringChecker.checkAccountNameAndPassword(accountName, password)) {
            return false;
        }
        MongoService mongoService = SpringUtils.getBean(MongoService.class);
        Criteria criteria = Criteria.where("accountName").is(accountName);
        AccountCollection accountCollection = mongoService.findOneByQuery(criteria, AccountCollection.class);
        // 账号名已被占用，不可重复注册
        if (accountCollection != null) {
            log.error("accountName {} already register.", accountName);
            return false;
        }
        accountCollection = new AccountCollection();
        long accountId = GenIdUtils.genAccountId();
        accountCollection.setAccountId(accountId);
        accountCollection.setAccountName(accountName);
        accountCollection.setPassword(password);
        boolean res = mongoService.insert(accountCollection);
        log.info("create account collection. accountId = {}, accountName = {}, res = {}", accountId, accountName, res);
        // 回包
        return res;
    }

    //endregion

    //region 账号登录
    @CMD(ProtoEnumMsg.CMD.ID.ACCOUNT_LOGIN_VALUE)
    public Pack onAccountLoginMsg(ConnectContext cc, AccountMsg.C2SAccountLogin up) {
        String accountName = up.getAccountName();
        String password = up.getPassword();
        boolean loginRes = accountLogin(cc, accountName.trim(), password.trim());
        AccountMsg.S2CAccountLogin.Builder builder = AccountMsg.S2CAccountLogin.newBuilder();
        builder.setSuccess(loginRes);
        if (loginRes) {
            builder.setAccountId(cc.getAccountId());
            // 查询该账号拥有的角色
            MongoService mongoService = SpringUtils.getBean(MongoService.class);
            Criteria criteria = Criteria.where("accountId").is(cc.getAccountId());
            List<SimpleRoleCollection> many = mongoService.findMany(criteria, SimpleRoleCollection.class);
            for (SimpleRoleCollection simpleRoleCollection : many) {
                AccountMsg.SimpleRole simpleRole = buildSimpleRoleMsg(simpleRoleCollection);
                builder.addRoles(simpleRole);
            }
        }
        return new Pack(ProtoEnumMsg.CMD.ID.ACCOUNT_LOGIN_VALUE, builder.build().toByteArray());
    }

    /**
     * 将db数据打包成Proto数据
     */
    private AccountMsg.SimpleRole buildSimpleRoleMsg(SimpleRoleCollection simpleRoleCollection) {
        AccountMsg.SimpleRole.Builder simpleRole = AccountMsg.SimpleRole.newBuilder();
        simpleRole.setRoleId(simpleRoleCollection.getRoleId());
        simpleRole.setName(simpleRoleCollection.getName());
        simpleRole.setLevel(simpleRoleCollection.getLevel());
        return simpleRole.build();
    }

    private boolean accountLogin(ConnectContext cc, String accountName, String password) {
        if (StringChecker.checkAccountNameAndPassword(accountName, password)) {
            return false;
        }

        MongoService mongoService = SpringUtils.getBean(MongoService.class);
        Criteria criteria = Criteria.where("accountName").is(accountName);
        AccountCollection accountCollection = mongoService.findOneByQuery(criteria, AccountCollection.class);
        if (accountCollection == null) {
            log.warn("login without account. accountName = {}", accountName);
            return false;
        } else if( !accountCollection.getPassword().equals(password) ) {
            log.warn("login password error. accountName = {}", accountName);
            return false;
        }
        log.info("login success. accountName = {}, accountId = {}", accountName, accountCollection.getAccountId());
        cc.setAccountId(accountCollection.getAccountId());
        cc.setAccountName(accountName);
        cc.setStatus(ConnectStatusEnum.ACCOUNT_LOGIN);
        // 添加accountId与actor映射
        ConnectActorManager connectActorManager = SpringUtils.getBean(ConnectActorManager.class);
        return connectActorManager.addConnectActorByAccountId(cc.getAccountId(), cc.getConnectId());
    }
    //endregion

    //region 创建角色
    @CMD(ProtoEnumMsg.CMD.ID.ROLE_CREATE_VALUE)
    public Pack onRoleCreateMsg(ConnectContext cc, AccountMsg.C2SRoleCreate up) {
        String name = up.getName();
        // TODO 重名判断 角色数量判断
        SimpleRoleCollection simpleRoleCollection = new SimpleRoleCollection();
        simpleRoleCollection.setAccountId(cc.getAccountId());
        simpleRoleCollection.setName(name);
        simpleRoleCollection.setLevel(1); // 默认1级
        long roleId = GenIdUtils.genRoleId();// 生成角色Id
        simpleRoleCollection.setRoleId(roleId);
        MongoService mongoService = SpringUtils.getBean(MongoService.class);
        mongoService.insert(simpleRoleCollection);
        AccountMsg.SimpleRole simpleRole = buildSimpleRoleMsg(simpleRoleCollection);
        AccountMsg.S2CRoleCreate.Builder builder = AccountMsg.S2CRoleCreate.newBuilder();
        builder.setSimpleRole(simpleRole);
        return new Pack(ProtoEnumMsg.CMD.ID.ROLE_CREATE_VALUE, builder.build().toByteArray());
    }
    //endregion

    //region 角色登录
    @CMD(ProtoEnumMsg.CMD.ID.ROLE_LOGIN_VALUE)
    public Pack onRoleLogin(ConnectContext cc, AccountMsg.C2SRoleLogin up) {
        long roleId = up.getRoleId();
        MongoService mongoService = SpringUtils.getBean(MongoService.class);
        SimpleRoleCollection simpleRoleCollection = mongoService.findById(roleId, SimpleRoleCollection.class);
        if (simpleRoleCollection == null) {
            log.error("没有该角色, roleId = {}, accountId = {}", roleId, cc.getAccountId());
            sendLoginError(cc);
            return null;
        }
        if (simpleRoleCollection.getAccountId() != cc.getAccountId()) {
            log.error("所选角色不归属于该账号, roleId = {}, accountId = {}, cc.accountId = {}", roleId, simpleRoleCollection.getAccountId(), cc.getAccountId());
            sendLoginError(cc);
            return null;
        }
        // 分配一个game服务id
        GameServerManger gameServerManger = SpringUtils.getBean(GameServerManger.class);
        GameServerContext gameServerContext = gameServerManger.randomGameServer();
        if (gameServerContext == null) {
            log.error("游戏服节点尚未初始化完毕。");
            sendLoginError(cc);
            return null;
        }
        // 通知到GameServer
        InnerServerMsg.L2GRoleLogin.Builder builder = InnerServerMsg.L2GRoleLogin.newBuilder();
        builder.setRoleId(roleId);
        Pack pack = new Pack(ProtoEnumMsg.CMD.ID.NOTIFY_GAME_ROLE_LOGIN_VALUE, builder);
        gameServerContext.getChannel().writeAndFlush(PackCodec.encode(pack));

        return null;
    }

    /**
     * 向客户端推送角色登录失败
     */
    private void sendLoginError(ConnectContext cc) {
        AccountMsg.S2CRoleLogin.Builder builder = AccountMsg.S2CRoleLogin.newBuilder();
        builder.setSuccess(false);
        Pack pack = new Pack(ProtoEnumMsg.CMD.ID.ROLE_LOGIN_VALUE, builder);
        cc.getClientChannel().writeAndFlush(PackCodec.encode(pack));
    }

}
